package edu.uic.cs.cs582.q_wordnet.data;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

import edu.mit.jwi.item.POS;
import edu.mit.jwi.item.SynsetID;
import edu.uic.cs.cs582.data.Polarity;
import edu.uic.cs.cs582.data.Synset;
import edu.uic.cs.cs582.misc.Assert;
import edu.uic.cs.cs582.misc.GeneralException;
import edu.uic.cs.cs582.misc.LogHelper;

public class SynsetsPolarityStore
{
	private static final Logger LOGGER = LogHelper
			.getLogger(SynsetsPolarityStore.class);

	private static final String[] FILE_NAMES = {
			"q-negative-30-0.3.txt",
			"q-positive-30-0.3.txt" };

	private static char hyphen = '-';

	private HashMap<String, Polarity> polarityBySynsetId = new HashMap<String, Polarity>();

	private HashMap<String, HashSet<Synset>> confilctSynsetsByWord = new HashMap<String, HashSet<Synset>>();

	private volatile static SynsetsPolarityStore instance = null;

	public static SynsetsPolarityStore getInstance()
	{
		if (instance == null)
		{
			synchronized (SynsetsPolarityStore.class)
			{
				if (instance == null)
				{
					instance = new SynsetsPolarityStore(FILE_NAMES);
				}
			}
		}

		return instance;
	}

	private SynsetsPolarityStore(String... polarityFiles)
	{
		Assert.notNull(polarityFiles);
		Assert.isTrue(polarityFiles.length > 0);

		initializeSynsetsPolarities(polarityFiles);
	}

	private void initializeSynsetsPolarities(String[] polarityFiles)
	{
		try
		{
			for (String fileName : polarityFiles)
			{
				Polarity polarity = parseFileNameIntoPolarity(fileName);

				List<String> lines = readLines(fileName);
				for (String line : lines)
				{
					String synsetId = parseEachLineIntoSynsetId(line);

					Polarity original = polarityBySynsetId.put(synsetId,
							polarity);

					if (LOGGER.isDebugEnabled())
					{
						LOGGER.debug("Parsed synset's polarity - <" + synsetId
								+ "=" + polarity + ">. ");
					}

					Assert.isTrue(original == null, "Duplicate Synset ID - "
							+ synsetId);

				}
			}
		}
		catch (IOException e)
		{
			throw new GeneralException(e);
		}
	}

	private Polarity parseFileNameIntoPolarity(String fileName)
	{
		fileName = fileName.toUpperCase(Locale.US);
		if (fileName.contains(Polarity.POSITIVE.toString()))
		{
			return Polarity.POSITIVE;
		}
		else if (fileName.contains(Polarity.NEGATIVE.toString()))
		{
			return Polarity.NEGATIVE;
		}
		else
		{
			throw new GeneralException("File[" + fileName
					+ "] is not supported. ");
		}
	}

	private String parseEachLineIntoSynsetId(String line)
	{
		String SynsetIdString = line.substring(0, line.indexOf(' '));
		int synsetIdInt = Integer.parseInt(SynsetIdString) % 100000000;

		char posTag = line.charAt(line.lastIndexOf('.') - 1);
		POS pos = POS.getPartOfSpeech(posTag);
		Assert.notNull(pos);

		return generateSynsetId(synsetIdInt, pos);
	}

	private String generateSynsetId(int synsetIdInt, POS pos)
	{
		StringBuilder sb = new StringBuilder();
		sb.append(SynsetID.synsetIDPrefix);
		sb.append(edu.mit.jwi.item.Synset.zeroFillOffset(synsetIdInt));
		sb.append(hyphen);
		sb.append(Character.toUpperCase(pos.getTag()));

		return sb.toString();
	}

	@SuppressWarnings("unchecked")
	private List<String> readLines(String fileName) throws IOException
	{
		InputStream in = null;
		try
		{
			in = SynsetsPolarityStore.class.getResourceAsStream(fileName);
			return IOUtils.readLines(in);
		}
		finally
		{
			IOUtils.closeQuietly(in);
		}
	}

	public Polarity getPolarity(String synsetId)
	{
		Assert.notNull(synsetId);

		return polarityBySynsetId.get(synsetId);
	}

	public boolean isPolarityKnown(String synsetId)
	{
		Assert.notNull(synsetId);
		return polarityBySynsetId.containsKey(synsetId);
	}

	public void recordConflictSynset(String word, Synset synset)
	{
		HashSet<Synset> confilctSynsets = confilctSynsetsByWord.get(word);
		if (confilctSynsets == null)
		{
			synchronized (SynsetsPolarityStore.class)
			{
				if ((confilctSynsets = confilctSynsetsByWord.get(word)) == null)
				{
					confilctSynsets = new HashSet<Synset>();
					confilctSynsetsByWord.put(word, confilctSynsets);
				}
			}
		}

		confilctSynsets.add(synset);
	}

	public void cleanConflictSynsetsFor(String word)
	{
		synchronized (SynsetsPolarityStore.class)
		{
			confilctSynsetsByWord.remove(word);
		}
	}

	public void cleanAllConflictSynsets()
	{
		synchronized (SynsetsPolarityStore.class)
		{
			confilctSynsetsByWord.clear();
		}
	}

	public Set<Synset> getConflictSynsetsFor(String word)
	{
		return confilctSynsetsByWord.get(word);
	}

	public Set<String> getAllConflictWords()
	{
		return confilctSynsetsByWord.keySet();
	}

}
